package cn.com.surker.production.controller;

import cn.com.surker.base.BaseEntity;
import cn.com.surker.common.Code;
import cn.com.surker.enums.EnumField;
import cn.com.surker.enums.ICodeDescEnum;
import cn.com.surker.enums.StateEnum;
import cn.com.surker.enums.production.indent.IsProEnum;
import cn.com.surker.enums.production.indent.ProTypeEnum;
import cn.com.surker.enums.production.indent.RoadTypeEnum;
import cn.com.surker.enums.production.invoice.*;
import cn.com.surker.handler.BCSStarter;
import cn.com.surker.handler.DisCoordinator;
import cn.com.surker.production.entity.*;
import cn.com.surker.production.entity.vo.DispatchVo;
import cn.com.surker.production.entity.vo.InvoiceHandleDetail;
import cn.com.surker.production.entity.vo.ProductionInvoiceVo;
import cn.com.surker.spring.RestResultDto;
import cn.com.surker.system.model.CacheUser;
import cn.com.surker.thread.ThreadPools;
import cn.com.surker.util.DateUtil;
import cn.com.surker.util.IpUtils;
import cn.com.surker.util.MathUtils;
import cn.com.surker.util.StringUtils;
import cn.com.surker.weight.service.ErpWeightOrderService;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.format.annotation.DateTimeFormat;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.BindingResult;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;

import javax.servlet.http.HttpServletRequest;
import javax.transaction.RollbackException;
import javax.validation.constraints.NotNull;
import java.net.InetAddress;
import java.util.*;
import java.util.concurrent.ThreadPoolExecutor;

/**
 * 发货单相关操作
 *
 * @author luotao
 * @date 2024/9/3
 */
@RestController
@RequestMapping("/pro/invoice")
public class InvoiceContrller extends ProBaseController {

	@Autowired
	private BCSStarter bcsStarter;
	@Autowired
	private DisCoordinator disCoordinator;
	@Autowired
	private ErpWeightOrderService erpWeightOrderService;

	/**
	 * 加载发货单相关的基础数据
	 *
	 * @return
	 */
	@RequestMapping(value = "/base/datas", method = RequestMethod.POST)
	@ResponseBody
	public RestResultDto baseDatas() {
		//  生产方式
		List<Map<String, Object>> billTypeList = ICodeDescEnum.getEnumDatas(InvoiceCreateTypeEnum.class);
		List<Map<String, Object>> proTypeList = new ArrayList<>();
		Map<String, Object> data1 = new HashMap<>();
		data1.put(EnumField.CODE, InvoiceCreateTypeEnum.MOVEMENT.getCode());
		data1.put(EnumField.DESC, InvoiceCreateTypeEnum.MOVEMENT.getDescription());
		proTypeList.add(data1);
		Map<String, Object> data2 = new HashMap<>();
		data2.put(EnumField.CODE, InvoiceCreateTypeEnum.EMPTY_TICKET.getCode());
		data2.put(EnumField.DESC, InvoiceCreateTypeEnum.EMPTY_TICKET.getDescription());
		proTypeList.add(data2);
		//  发货类型
		List<Map<String, Object>> invoiceTypeList = ICodeDescEnum.getEnumDatas(InvoiceSendTypeEnum.class);
		//  票据类型
		List<Map<String, Object>> receiptTypeList = ICodeDescEnum.getEnumDatas(ReceiptTypeEnum.class);
		//  特殊结算类型
		List<Map<String, Object>> specialClearTypeList = ICodeDescEnum.getEnumDatas(SpecialClearTypeEnum.class);
		//  道路类型
		List<Map<String, Object>> roadTypeList = ICodeDescEnum.getEnumDatas(RoadTypeEnum.class);
		Map<String, Object> res = new HashMap<>();
		res.put("createTypeList", proTypeList);
		res.put("billTypeList", billTypeList);
		res.put("invoiceTypeList", invoiceTypeList);
		res.put("receiptTypeList", receiptTypeList);
		res.put("specialClearTypeList", specialClearTypeList);
		res.put("roadTypeList", roadTypeList);
		return RestResultDto.success(res);
	}

	/**
	 * 查询发货单的总览信息,包含合同,任务单,泵送等信息,不包含转入转出
	 *
	 * @param id
	 * @return
	 */
	@RequestMapping(value = "/overview/{id}", method = RequestMethod.POST)
	@ResponseBody
	public RestResultDto overview(@PathVariable Long id) {
		if (id == null) {
			return RestResultDto.fail(Code.PARAM_EMPTY);
		}
		ProductionInvoiceVo productionInvoiceVo = invoiceService.queryOverview(erpDb, orgId, id);
		return RestResultDto.success(productionInvoiceVo);
	}

	@RequestMapping(value = "/detail/{id}", method = RequestMethod.POST)
	@ResponseBody
	public RestResultDto detail(@PathVariable Long id) {
		if (id == null) {
			return RestResultDto.fail(Code.PARAM_EMPTY);
		}
		ProductionInvoiceVo productionInvoiceVo = invoiceService.queryDetail(erpDb, orgId, id);
		return RestResultDto.success(productionInvoiceVo);
	}

	/**
	 * 分页数据,如果是列表查询,因为发货单数据过多,因此也调用此接口,仅查询前50条数据
	 *
	 * @return
	 */
	@RequestMapping(value = "/page", method = RequestMethod.POST)
	@ResponseBody
	public RestResultDto page(String startTime, String endTime, String searchVal, String searchVal2, Integer createType, Integer process, Integer isLinked,
							  Integer isAdd, Integer pageIndex, Integer limit) {
		if (pageIndex == null) {
			pageIndex = 1;
		}
		if (limit == null) {
			limit = 50;
		}
		IPage<ProductionInvoiceVo> page = invoiceService.selectFullPage(erpDb, startTime, endTime, searchVal, searchVal2, null, null, createType, process, isAdd, isLinked, pageIndex, limit);
		return RestResultDto.success(page);
	}

	/**
	 * 查询等待接收,等待生产,生产中的发货单数据
	 *
	 * @return
	 */
	@RequestMapping(value = "/dis/page", method = RequestMethod.POST)
	@ResponseBody
	public RestResultDto disPage() {
		Object o = null;
		try {
			o = memoryCache.get(DisCoordinator.disInvKey);
		} catch (Exception e) {
			e.printStackTrace();
		}
		if (o == null) {
			o = disCoordinator.disInvList();
		}
		return RestResultDto.success(o);
	}


	/**
	 * 查询近10车的发货单
	 *
	 * @return
	 */
	@RequestMapping(value = "/nearly/list", method = RequestMethod.POST)
	@ResponseBody
	public RestResultDto nearlyList() {
		Object o = null;
		try {
			o = memoryCache.get(DisCoordinator.nearlyInvKey);
		} catch (Exception e) {
			e.printStackTrace();
		}
		if (o == null) {
			o = disCoordinator.nearlyInvList();
		}
		return RestResultDto.success(o);
	}

	/**
	 * 查询正在运输中的发货单
	 *
	 * @param startTime
	 * @param endTime
	 * @param searchVal
	 * @param createType
	 * @param pageIndex
	 * @param limit
	 * @return
	 */
	@RequestMapping(value = "/tran/page", method = RequestMethod.POST)
	@ResponseBody
	@Deprecated
	public RestResultDto tranPage(String startTime, String endTime, String searchVal,
								  Integer createType, Integer pageIndex, Integer limit) {
		if (pageIndex == null) {
			pageIndex = 1;
		}
		if (limit == null) {
			limit = 50;
		}
		List<Integer> processList = new ArrayList<>();
		processList.add(InvoiceProcessEnum.PRO_COMPLETE.getCode());
		processList.add(InvoiceProcessEnum.TRAN.getCode());
		IPage<ProductionInvoiceVo> page = invoiceService.selectFullPageV2(erpDb, startTime, endTime, searchVal, null, null, createType, processList, pageIndex, limit);
		return RestResultDto.success(page);
	}

	/**
	 * 手动新增发货单
	 *
	 * @return
	 */
	@RequestMapping(value = "/add-hand", method = RequestMethod.PUT)
	@ResponseBody
	@Transactional(rollbackFor = RollbackException.class, propagation = Propagation.REQUIRED)
	public RestResultDto add(@Validated ProductionInvoice invoice, BindingResult results) {
		if (invoice == null) {
			return RestResultDto.fail(Code.PARAM_EMPTY);
		}
		if (results.hasErrors()) {
			String defaultMessage = results.getFieldError().getDefaultMessage();
			return RestResultDto.fail(defaultMessage);
		}
		CacheUser user = getUser();
		//	验证任务单
		@NotNull(message = "请选择任务单") Long indentId = invoice.getIndentId();
		ProductionIndent indent = indentService.getById(indentId);
		if (!BaseEntity.validateExist(indent)) {
			return RestResultDto.fail("该任务单不存在或已经删除");
		}
		if (!ProTypeEnum.WORK.getCode().equals(indent.getType())) {
			return RestResultDto.fail("该任务单不是正供状态");
		}
		//	写入车辆信息
		@NotNull(message = "请选择运输车辆") Long carId = invoice.getCarId();
		TranCar tranCar = tranCarService.getById(carId);
		if (tranCar == null) {
			return RestResultDto.fail("该运输车辆不存在,请重新选择正确的车辆");
		}
		Integer invoiceType = invoice.getInvoiceType();
		if (InvoiceSendTypeEnum.CONCRETE.getCode().equals(invoiceType) || InvoiceSendTypeEnum.SAND.getCode().equals(invoiceType)) {
			//	必选生产线,拌台操作员
			Long mixId = invoice.getMixId();
			if (mixId == null) {
				return RestResultDto.fail("请选择生产线");
			}
			Long mixUser = invoice.getMixUser();
			if (mixUser == null) {
				return RestResultDto.fail("请选择拌台操作员");
			}
		}
		ProductionIndentPump indentPump = indentPumpService.queryDefaultIndentId(erpDb, indentId);
		if (indentPump == null) {
			//	如果任务单没有泵送设备
			return RestResultDto.fail("任务单没有设置浇筑方式或泵送设备");
		}
		Long linkPumpId = indentPump.getId();
		Date nowDate = new Date();
		invoice.setOrgId(orgId);
		invoice.initCode(orgCode);
		invoice.setDistance(indent.getDistance());
		//	发货单状态默认正常
		invoice.setProcess(InvoiceProcessEnum.TRAN_COMPLETE.getCode());
		//	创建时间
		invoice.setCreateTime(nowDate);
		invoice.setCreator(user.getName());
		invoice.setLinkPumpId(linkPumpId);
		invoice.setStatus(StateEnum.ENABLED.getCode());
		invoiceService.save(invoice);
		//	判断该任务单是否有实际开盘时间
		Date actualStartTime = indent.getActualStartTime();
		if (actualStartTime == null) {
			//	设置实际开盘时间并更新数据
			indent.setActualStartTime(nowDate);
			indent.setIsPro(IsProEnum.WORK.getCode());
			indentService.updateById(indent);
		}
		return RestResultDto.success();
	}


	/**
	 * 更新发货单
	 *
	 * @param id
	 * @param sendStere
	 * @param carId
	 * @param driver
	 * @param dispatcher
	 * @param dispatcherName
	 * @param mixUser
	 * @param mixUserName
	 * @param createType
	 * @param calcTranStere
	 * @param proTime
	 * @param receiptType
	 * @param specialClearType
	 * @param roadType
	 * @param pumpDeviceId
	 * @param remark
	 * @param tranRemark
	 * @param pumpRemark
	 * @return
	 */
	@RequestMapping(value = "/update/{id}", method = RequestMethod.PUT)
	@ResponseBody
	@Transactional(rollbackFor = RollbackException.class, propagation = Propagation.REQUIRED)
	public RestResultDto update(@PathVariable Long id, Double sendStere, Long carId, String driver, Long dispatcher, String dispatcherName, Long mixUser, Integer isAdd,
								String mixUserName, Integer createType, Integer calcTranStere, @DateTimeFormat(pattern = DateUtil.DATE_DETAIL_FORMATTER) Date proTime, Integer receiptType,
								Integer specialClearType, Integer roadType, Long pumpDeviceId, String remark, String tranRemark, String pumpRemark, Integer totalCar, Double totalStere) {
		if (id == null) {
			return RestResultDto.fail("请选择正确的数据");
		}
		//	验证参照
		if (sendStere == null || sendStere <= 0d) {
			return RestResultDto.fail("请输入发货方量");
		}
		if (carId == null) {
			return RestResultDto.fail("请选择运输车辆");
		}
		if (dispatcher == null) {
			return RestResultDto.fail("请选择调度员");
		}
		/*if (mixUser == null) {
			return RestResultDto.fail("请选择拌台操作员");
		}*/
		/*if (isAdd == null) {
			return RestResultDto.fail("请选择是否累计");
		}
		if (createType == null) {
			return RestResultDto.fail("请选择生产类型");
		}*/

		/*if (roadType == null) {
			return RestResultDto.fail("请选择运输道路类型");
		}*/
		ProductionInvoice oldInvoice = invoiceService.getById(id);
		if (!BaseEntity.validateExist(oldInvoice)) {
			return RestResultDto.fail("发货单不存在或者已经被删除");
		}
		//	写入车辆信息
		TranCar tranCar = tranCarService.getById(carId);
		if (tranCar == null) {
			return RestResultDto.fail("该运输车辆不存在,请重新选择正确的车辆");
		}
		@NotNull(message = "请选择运输车辆") Long oldCarId = oldInvoice.getCarId();
		CacheUser user = getUser();
		String userName = user.getName();
		oldInvoice.setSendStere(sendStere);
		oldInvoice.setCarId(carId);
		oldInvoice.setDriver(driver);
		oldInvoice.setDispatcher(dispatcher);
		oldInvoice.setDispatcherName(dispatcherName);
		oldInvoice.setMixUser(mixUser);
		oldInvoice.setMixUserName(mixUserName);
		oldInvoice.setCreateType(createType);
		oldInvoice.setRoadType(roadType);
		oldInvoice.setCalcTranStere(calcTranStere);
		oldInvoice.setReceiptType(receiptType);
		oldInvoice.setSpecialClearType(specialClearType);
		oldInvoice.setProTime(proTime);
		oldInvoice.setRemark(remark);
		oldInvoice.setTranRemark(tranRemark);
		oldInvoice.setPumpRemark(pumpRemark);
		// oldInvoice.setPumpDevice(pumpDeviceId);
		oldInvoice.setIsAdd(isAdd);
		oldInvoice.setPopUp(PopEnum.NOT_REQUIRED.getCode());
		oldInvoice.setUpdator(userName);
		//	设置累计车次和方量
		oldInvoice.setTotalCar(totalCar);
		oldInvoice.setTotalStere(totalStere);
		//	更新数据
		invoiceService.updateById(oldInvoice);
		// 判定车辆是否变动
		ThreadPoolExecutor commonThreadPool = ThreadPools.getCommonThreadPool();
		commonThreadPool.execute(new Runnable() {
			@Override
			public void run() {
				if (!oldCarId.equals(carId)) {
					//	变动了,需要重新过磅
					Integer process = oldInvoice.getProcess();
					if (InvoiceProcessEnum.PRO_COMPLETE.getCode().equals(process)) {
						//	表示生产完成,出厂前,删除旧的过磅单
						erpWeightOrderService.delTongWeight(oldInvoice.getCode());
						//	生成新的过磅单记录
						ProductionInvoiceVo targetInvoiceVo = invoiceService.queryOverview(erpDb, orgId, id);
						erpWeightOrderService.beginWeight(orgId, orgName, dsId, targetInvoiceVo, "换车过磅");
					}
				}
			}
		});
		return RestResultDto.success();
	}

	/**
	 * 整车转料,转料到本站
	 *
	 * @param sourceInvId      源发货单
	 * @param backType         退料类型(站内退料,站外退料,关联枚举:BackTypeEnum)
	 * @param targetOrgId      目标站点
	 * @param indentId         目标任务单
	 * @param receiptType      转料用,发货单票据类型
	 * @param specialClearType 转料用,特殊结算类型
	 * @param calcTranStere    转料用,是否计算运输方量
	 * @param tranRemark       转料用,运输备注
	 * @param pumpRemark       转料用,泵送备注
	 * @param remark           备注
	 * @return
	 */
	@RequestMapping(value = "/tran/own/{sourceInvId}", method = RequestMethod.PUT)
	@ResponseBody
	@Transactional(rollbackFor = RollbackException.class, propagation = Propagation.REQUIRED)
	public RestResultDto invoiceWholeTran(@PathVariable Long sourceInvId, Integer backType, Long targetOrgId, Long indentId, Integer isAdd,
										  Integer receiptType, Integer specialClearType, Integer calcTranStere, String tranRemark, String pumpRemark, String remark,
										  String operateRemark, Double targetSendStere, Integer totalCar, Double totalStere) {
		if (sourceInvId == null) {
			return RestResultDto.fail("请选择源发货单");
		}
		if (StringUtils.isBlank(operateRemark)) {
			return RestResultDto.fail("请输入转料原因");
		}
		if (targetSendStere == null || targetSendStere <= 0) {
			return RestResultDto.fail("请输入新车发货方量");
		}
		ProductionInvoice sourceInvoice = invoiceService.getById(sourceInvId);
		if (!BaseEntity.validateExist(sourceInvoice)) {
			return RestResultDto.fail("该发货单不存在或已经被删除");
		}
		BackTypeEnum backTypeEnum = ICodeDescEnum.getEnum(BackTypeEnum.class, backType);
		if (backTypeEnum == null) {
			return RestResultDto.fail("请选择正确的退料类型(站内退料/站外退料)");
		}
		//	判断任务单是否同一个
		Long oldIndentId = sourceInvoice.getIndentId();
		if (oldIndentId.equals(indentId)) {
			return RestResultDto.fail("目标任务单和源任务单为同一个,不允许操作");
		}
		/*if (TranTypeEnum.WHOLE_TRAN.getCode().equals(tranType)) {
			//	如果是整车转操作,验证发货单是否有过其他转料,作废记录
			List<BackOperate> sourInOperateList = backOperateService.findSourceInvoice(orgId, sourceInvId, null, null, ShipTypeEnum.INVOICE_SCRAP.getCode(),
				ShipTypeEnum.INVOICE_TRAN_TO_OTHER.getCode(), ShipTypeEnum.INVOICE_TRAN_TO_OWN.getCode());
			if (sourInOperateList != null && sourInOperateList.size() > 0) {
				return RestResultDto.fail("该发货单下已经有过操作记录,不允许进行整车操作");
			}
		}*/
		//	发货单操作的转料仅仅只能整车转料
		List<InvoiceHandle> sourInOperateList = invoiceHandleService.findSourceInvoice(orgId, sourceInvId, null, null, ShipTypeEnum.INVOICE_SCRAP.getCode(),
			ShipTypeEnum.INVOICE_TRAN_TO_OTHER.getCode(), ShipTypeEnum.INVOICE_TRAN_TO_OWN.getCode(), ShipTypeEnum.INVOICE_SPLIT_TRAN_TO_OTHER.getCode());
		if (sourInOperateList != null && sourInOperateList.size() > 0) {
			return RestResultDto.fail("该发货单下已经有过操作记录,不允许进行整车转料操作");
		}
		//	验证任务单
		if (indentId == null) {
			return RestResultDto.fail("请选择目标任务单");
		}
		ProductionIndent indent = indentService.getById(indentId);
		if (!BaseEntity.validateExist(indent)) {
			return RestResultDto.fail("目标任务单不存在或者已经被删除");
		}
		//	判断任务单是否正供
		Integer indentType = indent.getType();
		if (!ProTypeEnum.WORK.getCode().equals(indentType)) {
			return RestResultDto.fail("该任务单不是正供状态");
		}
		Long newLevelId = indent.getStrengthId();
		//	新任务单的合同id
		Long conId = indent.getConId();
		//	发货方量
		Double sendStere = sourceInvoice.getSendStere();
		//	验证合同是否允许操作
		RestResultDto conValRes = validateConPro(conId, newLevelId, sendStere);
		if (RestResultDto.SUCCESS != conValRes.getCode()) {
			//	验证失败
			return conValRes;
		}
		ProductionIndent oldIndent = indentService.getById(oldIndentId);
		Long oldLevelId = oldIndent.getStrengthId();
		//	判断是否降级处理
		boolean strengthLower = strengthLower(oldLevelId, newLevelId);
		if (!strengthLower) {
			return RestResultDto.fail("整车转料标号只允许降级处理");
		}
		ProductionIndentPump indentPump = indentPumpService.queryDefaultIndentId(erpDb, indentId);
		if (indentPump == null) {
			//	如果任务单没有泵送设备
			return RestResultDto.fail("任务单没有设置浇筑方式或泵送设备");
		}
		Integer oldProcess = sourceInvoice.getProcess();
		//	登录用户
		CacheUser loginUser = getUser();
		Date nowDate = new Date();
		//	流程:1 创建新发货单;2:创建关联关系
		ProductionInvoice targetInvoice = new ProductionInvoice();
		targetInvoice.setOrgId(sourceInvoice.getOrgId());
		// targetInvoice.setCode(ProductionInvoice.initCode(orgCode));
		targetInvoice.initCode(orgCode);
		targetInvoice.setIndentId(indentId);
		targetInvoice.setInvoiceType(sourceInvoice.getInvoiceType());
		targetInvoice.setCreateType(InvoiceCreateTypeEnum.TRAN.getCode());
		//	状态为原发货单的状态
		targetInvoice.setProcess(oldProcess);
		// targetInvoice.setPumpDevice(indent.getPumpDevice());
		targetInvoice.setDistance(sourceInvoice.getDistance());
		// targetInvoice.setProTime(sourceInvoice.getProTime());
		targetInvoice.setCarId(sourceInvoice.getCarId());
		targetInvoice.setDriver(sourceInvoice.getDriver());
		targetInvoice.setMixId(sourceInvoice.getMixId());
		targetInvoice.setDispatcher(loginUser.getId());
		targetInvoice.setDispatcherName(loginUser.getName());
		targetInvoice.setSendStere(targetSendStere);
		targetInvoice.setWeightStere(sourceInvoice.getWeightStere());
		targetInvoice.setMixId(1l);
		targetInvoice.setMixUserName("转料");
		targetInvoice.setRoadType(indent.getRoadType());
		targetInvoice.setReceiptType(receiptType);
		targetInvoice.setSpecialClearType(specialClearType);
		targetInvoice.setCalcTranStere(calcTranStere);
		targetInvoice.setTranRemark(tranRemark);
		targetInvoice.setPumpRemark(pumpRemark);
		targetInvoice.setRemark(remark);
		targetInvoice.setCreator(loginUser.getName());
		targetInvoice.setCreateTime(nowDate);
		targetInvoice.setStatus(StateEnum.ENABLED.getCode());
		//	更新发货单生产时间
		targetInvoice.setProTime(nowDate);
		//	设置累计车次和方量
		targetInvoice.setTotalCar(totalCar);
		targetInvoice.setTotalStere(totalStere);
		targetInvoice.setLinkPumpId(indentPump.getId());
		//	保存发货单数据
		invoiceService.save(targetInvoice);
		Long targetInvoiceId = targetInvoice.getId();
		//	更新源发货单数据
		sourceInvoice.setProcess(InvoiceProcessEnum.CAR_TRAN_OUT.getCode());
		sourceInvoice.setIsAdd(isAdd);
		if (isAdd != null && isAdd == 1) {
			//	不累计判定为作废
			sourceInvoice.setProcess(InvoiceProcessEnum.SCRAP.getCode());
			sourceInvoice.setTotalCar(null);
			sourceInvoice.setTotalStere(null);
			operateRemark += ";整车转料原车作废";
		}
		invoiceService.updateById(sourceInvoice);
		//	记录关联数据
		InvoiceHandle backOperate = new InvoiceHandle();
		backOperate.setSourceInvoiceId(sourceInvId);
		backOperate.setSourceOrgId(orgId);
		backOperate.setTargetOrgId(orgId);
		backOperate.setTargetInvoiceId(targetInvoiceId);
		backOperate.setOperateType(ShipTypeEnum.INVOICE_TRAN_TO_OWN.getCode());
		backOperate.setTranType(TranTypeEnum.WHOLE_TRAN.getCode());
		backOperate.setReType(backType);
		backOperate.setOperateStere(sourceInvoice.getSendStere());
		backOperate.setWeightStere(sourceInvoice.getWeightStere());
		backOperate.setCreator(loginUser.getName());
		backOperate.setCreateTime(nowDate);
		backOperate.setStatus(StateEnum.ENABLED.getCode());
		backOperate.setRemark(operateRemark);
		invoiceHandleService.save(backOperate);
		//	判断该任务单是否有实际开盘时间
		Date actualStartTime = indent.getActualStartTime();
		if (actualStartTime == null) {
			//	设置实际开盘时间并更新数据
			indent.setActualStartTime(nowDate);
			indent.setIsPro(IsProEnum.WORK.getCode());
			indentService.updateById(indent);
		}
		//	判定旧的发货单是否过磅,如果过磅了则不操作,如果没有过磅,则要删除旧的过磅,然后新增一条过磅
		ThreadPoolExecutor commonThreadPool = ThreadPools.getCommonThreadPool();
		commonThreadPool.execute(new Runnable() {
			@Override
			public void run() {
				if (InvoiceProcessEnum.PRO_COMPLETE.getCode().equals(oldProcess)) {
					//	表示生产完成,出厂前,删除旧的过磅单
					erpWeightOrderService.delTongWeight(sourceInvoice.getCode());
					//	生成新的过磅单记录
					ProductionInvoiceVo targetInvoiceVo = invoiceService.queryOverview(erpDb, orgId, targetInvoiceId);
					erpWeightOrderService.beginWeight(orgId, orgName, dsId, targetInvoiceVo, "整车转料过磅");
				}
			}
		});
		return RestResultDto.success();
	}

	/**
	 * 发货单的分车转料
	 *
	 * @param sourceInvId 原发货单
	 * @param backType    退料类型
	 * @param tranStere   转出方量
	 * @param tranRemark  转料备注
	 * @return
	 */
	@RequestMapping(value = "/tran/split/own/{sourceInvId}", method = RequestMethod.PUT)
	@ResponseBody
	@Transactional(rollbackFor = RollbackException.class, propagation = Propagation.REQUIRED)
	public RestResultDto invoiceSplitTran(@PathVariable Long sourceInvId, Integer backType, Long targetInvId, Integer isAdd,
										  Double tranStere, Double sourceSendStere, Double targetSendStere, String tranRemark) {
		if (sourceInvId == null) {
			return RestResultDto.fail("请选择源发货单");
		}
		if (targetInvId == null) {
			return RestResultDto.fail("请选择目标发货单");
		}
		if (sourceInvId.equals(targetInvId)) {
			return RestResultDto.fail("转料为同一个发货单");
		}
		if (tranStere == null || tranStere < 0) {
			return RestResultDto.fail("请输入转料方量");
		}
		if (StringUtils.isBlank(tranRemark)) {
			return RestResultDto.fail("请输入转料原因");
		}
		if (isAdd == null) {
			return RestResultDto.fail("请选择源发货单是否作废");
		}
		if (sourceSendStere == null || sourceSendStere < 0) {
			return RestResultDto.fail("请输入源发货单发货方量");
		}
		if (targetSendStere == null || targetSendStere < 0) {
			return RestResultDto.fail("请输入目标发货单发货方量");
		}
		BackTypeEnum backTypeEnum = ICodeDescEnum.getEnum(BackTypeEnum.class, backType);
		if (backTypeEnum == null) {
			return RestResultDto.fail("请选择正确的退料类型(站内退料/站外退料)");
		}
		ProductionInvoiceVo sourceInvoice = invoiceService.queryOverview(erpDb, orgId, sourceInvId);
		if (!BaseEntity.validateExist(sourceInvoice)) {
			return RestResultDto.fail("源发货单不存在或已经被删除");
		}
		ProductionInvoice targetInvoice = invoiceService.getById(targetInvId);
		if (!BaseEntity.validateExist(targetInvoice)) {
			return RestResultDto.fail("目标发货单不存在或已经被删除");
		}
		//	查询源发货单下的发货方量
		Double allProStere = sourceInvoice.getProStere();
		if (allProStere == null || allProStere < 0d) {
			return RestResultDto.fail("源发货单的生产方量不足0方,请检查");
		}
		Double operatedStere = 0d;
		//	查询发货单下的转料记录
		List<InvoiceHandle> sourInOperateList = invoiceHandleService.findSourceInvoice(orgId, sourceInvId, null, null, ShipTypeEnum.INVOICE_SCRAP.getCode(),
			ShipTypeEnum.INVOICE_TRAN_TO_OTHER.getCode(), ShipTypeEnum.INVOICE_TRAN_TO_OWN.getCode(), ShipTypeEnum.INVOICE_SPLIT_TRAN_TO_OTHER.getCode());
		if (sourInOperateList != null && sourInOperateList.size() > 0) {
			for (InvoiceHandle invoiceHandle : sourInOperateList) {
				Double expectStere = invoiceHandle.getOperateStere();
				operatedStere = MathUtils.add(operatedStere, expectStere);
			}
		}
		//	计算剩余方量
		Double surplusStere = MathUtils.subtract(allProStere, operatedStere);
		//	比较剩余方量和转料方量
		if (surplusStere < tranStere) {
			return RestResultDto.fail("该发货的生产方量(" + allProStere + ")方,已操作(" + operatedStere + ")方,仅剩余(" + surplusStere + ")方,不足转料方量(" + tranStere + ")方");
		}
		Long sourceIndentId = sourceInvoice.getIndentId();
		ProductionIndent sourceIndent = indentService.getById(sourceIndentId);
		if (!BaseEntity.validateExist(sourceIndent)) {
			return RestResultDto.fail("源任务单不存在或者已经被删除");
		}
		Long targetIndentId = targetInvoice.getIndentId();
		ProductionIndent targetIndent = indentService.getById(targetIndentId);
		if (!BaseEntity.validateExist(targetIndent)) {
			return RestResultDto.fail("目标任务单不存在或者已经被删除");
		}
		Long conId = targetIndent.getConId();
		Long oldLevelId = sourceIndent.getStrengthId();
		//	目标发货单的强度等级
		Long newLevelId = targetIndent.getStrengthId();
		//	目标发货单就的生产方量
		Double sendStere = targetInvoice.getSendStere();
		//	计算新增的方量
		Double newAddStere = MathUtils.subtract(targetSendStere, sendStere);
		//	验证合同是否允许操作
		RestResultDto conValRes = validateConPro(conId, newLevelId, newAddStere);
		if (RestResultDto.SUCCESS != conValRes.getCode()) {
			//	验证失败
			return conValRes;
		}
		//	判断是否降级处理
		boolean strengthLower = strengthLower(oldLevelId, newLevelId);
		if (!strengthLower) {
			return RestResultDto.fail("转料标号只允许降级处理");
		}
		//	登录用户
		CacheUser loginUser = getUser();
		Date nowDate = new Date();
		//	更新源发货单数据
		sourceInvoice.setProcess(InvoiceProcessEnum.CAR_SPLIT_TRAN_OUT.getCode());
		sourceInvoice.setSendStere(sourceSendStere);
		//	判定累加是否变动
		sourceInvoice.setIsAdd(isAdd);
		if (isAdd != null && isAdd == 1) {
			//	不累计判定为作废
			sourceInvoice.setProcess(InvoiceProcessEnum.SCRAP.getCode());
			sourceInvoice.setTotalCar(null);
			sourceInvoice.setTotalStere(null);
			tranRemark += ";分车转料原车作废";
		}
		invoiceService.updateById(sourceInvoice);
		//	记录关联数据
		InvoiceHandle backOperate = new InvoiceHandle();
		backOperate.setSourceInvoiceId(sourceInvId);
		backOperate.setSourceOrgId(orgId);
		backOperate.setTargetOrgId(orgId);
		backOperate.setTargetInvoiceId(targetInvId);
		backOperate.setOperateType(ShipTypeEnum.INVOICE_TRAN_TO_OWN.getCode());
		backOperate.setTranType(TranTypeEnum.BATCH_TRAN.getCode());
		backOperate.setReType(backType);
		//	先写默认数据,后面在该
		backOperate.setOperateStere(tranStere);
		backOperate.setExpectStere(tranStere);
		backOperate.setWeightStere(tranStere);
		backOperate.setCreator(loginUser.getName());
		backOperate.setCreateTime(nowDate);
		backOperate.setStatus(StateEnum.ENABLED.getCode());
		backOperate.setRemark(tranRemark);
		invoiceHandleService.save(backOperate);
		//	更新目标发货单数据
		targetInvoice.setSendStere(targetSendStere);
		//	更新发货单生产时间
		targetInvoice.setProTime(nowDate);
		invoiceService.updateById(targetInvoice);
		return RestResultDto.success();
	}

	/**
	 * 作废发货单数据
	 *
	 * @return
	 */
	@RequestMapping(value = "/scrap/{id}", method = RequestMethod.DELETE)
	@ResponseBody
	@Transactional(rollbackFor = RollbackException.class, propagation = Propagation.REQUIRED)
	public RestResultDto scrap(@PathVariable Long id, String remark, HttpServletRequest request) {
		if (id == null) {
			return RestResultDto.fail(Code.PARAM_EMPTY);
		}
		if (StringUtils.isBlank(remark)) {
			return RestResultDto.fail("请输入作废原因");
		}
		ProductionInvoice invoice = invoiceService.getById(id);
		if (!BaseEntity.validateExist(invoice)) {
			return RestResultDto.fail("发货单数据不存在或者被删除");
		}
		//	查询是否有退剩转记录
		List<InvoiceHandle> backOperates = invoiceHandleService.findBySouInvId(orgId, id);
		if (backOperates != null && backOperates.size() > 0) {
			return RestResultDto.fail("该发货单存在转出数据,请先清除转料数据");
		}
		//	转入记录
		// List<InvoiceHandle> inOperates = invoiceHandleService.findByTarInvId(orgId, id);

		//invoice.setStatus(StateEnum.DISABLED.getCode());
		//	创建一条作废记录
		CacheUser user = getUser();
		//	创建数据
		InvoiceHandle backOperate = new InvoiceHandle();
		backOperate.setSourceOrgId(orgId);
		backOperate.setSourceInvoiceId(id);
		backOperate.setOperateType(ShipTypeEnum.INVOICE_SCRAP.getCode());
		backOperate.setReType(BackTypeEnum.STATION_TRAN.getCode());
		/*//	车辆装载量
		Double carLoadStere = invoice.getCarLoadStere();
		backOperate.setOperateStere(carLoadStere);*/
		//	生产方量
		Double proStere = invoiceService.queryProStere(erpDb, id);
		backOperate.setOperateStere(proStere);
		//	过磅方量
		backOperate.setWeightStere(invoice.getWeightStere());
		backOperate.setCreator(user.getName());
		backOperate.setCreateTime(new Date());
		backOperate.setRemark(remark);
		String ipAddress1 = IpUtils.getIpAddress(request);
		String ipAddress2 = "";
		try {
			InetAddress localHost = InetAddress.getLocalHost();
			ipAddress2 = localHost.getHostAddress();
		} catch (Exception e) {
			e.printStackTrace();
		}
		String ipAddress = "(" + ipAddress1 + "/" + ipAddress2 + ")";
		backOperate.addRemark(ipAddress);
		//	保存数据
		invoiceHandleService.save(backOperate);
		//	修改发货单状态
		invoice.setProcess(InvoiceProcessEnum.SCRAP.getCode());
		invoice.setTotalCar(null);
		invoice.setTotalStere(null);
		//	作废不累计
		invoice.setIsAdd(1);
		invoiceService.updateById(invoice);
		return RestResultDto.success();
	}

	/**
	 * 查询发货单的处理记录
	 *
	 * @param invId
	 * @return
	 */
	@RequestMapping(value = "/handle/list/{invId}", method = RequestMethod.POST)
	@ResponseBody
	public RestResultDto handleList(@PathVariable Long invId) {
		if (invId == null) {
			return RestResultDto.fail("请选择发货单");
		}
		List<InvoiceHandleDetail> handleDetails = invoiceHandleService.handleList(erpDb, orgId, invId);
		return RestResultDto.success(handleDetails);
	}

	/**
	 * 发货单的派单列表
	 *
	 * @param invId
	 * @return
	 */
	@RequestMapping(value = "/dispatch/list/{invId}", method = RequestMethod.POST)
	@ResponseBody
	public RestResultDto dispatchList(@PathVariable Long invId) {
		if (invId == null) {
			return RestResultDto.fail("请选择发货单");
		}
		List<DispatchVo> dispatches = indentDispatchService.queryFullByInvoice(erpDb, invId, null);
		return RestResultDto.success(dispatches);
	}

	/**
	 * 发货单作废后的恢复操作
	 *
	 * @param invId
	 * @return
	 */
	@RequestMapping(value = "/scrap/restore/{invId}", method = RequestMethod.PUT)
	@ResponseBody
	@Transactional(rollbackFor = RollbackException.class, propagation = Propagation.REQUIRED)
	public RestResultDto scrapRestore(@PathVariable Long invId, String remark, HttpServletRequest request) {
		if (invId == null) {
			return RestResultDto.fail("请选择发货单");
		}
		ProductionInvoice invoice = invoiceService.getById(invId);
		if (!BaseEntity.validateExist(invoice)) {
			return RestResultDto.fail("发货单不存在或者被删除");
		}
		//	发货单状态
		Integer process = invoice.getProcess();
		if (!InvoiceProcessEnum.SCRAP.getCode().equals(process)) {
			return RestResultDto.fail("发货单不是作废状态,不允许操作");
		}
		//	更改发货单状态
		invoice.setProcess(InvoiceProcessEnum.TRAN_COMPLETE.getCode());
		//	恢复为累计
		invoice.setIsAdd(0);
		invoiceService.updateById(invoice);
		//	创建一条恢复记录
		CacheUser user = getUser();
		//	创建数据
		InvoiceHandle backOperate = new InvoiceHandle();
		backOperate.setSourceOrgId(orgId);
		backOperate.setSourceInvoiceId(invId);
		backOperate.setReType(BackTypeEnum.STATION_TRAN.getCode());
		backOperate.setOperateType(ShipTypeEnum.INVOICE_RESTORE.getCode());
		backOperate.setOperateStere(invoice.getSendStere());
		//	过磅方量
		backOperate.setWeightStere(invoice.getWeightStere());
		backOperate.setCreator(user.getName());
		backOperate.setCreateTime(new Date());
		backOperate.setRemark(remark);
		String ipAddress1 = IpUtils.getIpAddress(request);
		String ipAddress2 = "";
		try {
			InetAddress localHost = InetAddress.getLocalHost();
			ipAddress2 = localHost.getHostAddress();
		} catch (Exception e) {
			e.printStackTrace();
		}
		String ipAddress = "(" + ipAddress1 + "/" + ipAddress2 + ")";
		backOperate.addRemark(ipAddress);
		//	保存数据
		invoiceHandleService.save(backOperate);
		//	将原本的作废记录修改为不统计的作废
		UpdateWrapper<InvoiceHandle> uw = new UpdateWrapper<>();
		uw.eq("status", StateEnum.ENABLED.getCode());
		uw.eq("operate_type", ShipTypeEnum.INVOICE_SCRAP.getCode());
		uw.eq("source_invoice_id", invId);
		uw.set("operate_type", ShipTypeEnum.INVOICE_SCRAP_NO_CENUS.getCode());
		boolean update = invoiceHandleService.update(uw);
		return RestResultDto.success();
	}

	/**
	 * 重新匹配发货单缺失的消耗仓位
	 *
	 * @param invoiceId
	 * @return
	 */
	@RequestMapping(value = "/sync/storage/{invoiceId}", method = RequestMethod.PUT)
	@ResponseBody
	public RestResultDto syncExpend(@PathVariable Long invoiceId) {
		if (invoiceId == null) {
			return RestResultDto.fail(Code.PARAM_EMPTY);
		}
		//	查询现在全部的派单记录
		List<ProductionIndentDispatch> dispatches = indentDispatchService.queryByInvoice(invoiceId);
		for (ProductionIndentDispatch dispatch : dispatches) {
			Long dispatchId = dispatch.getId();
			bcsStarter.startMaterial(dispatchId);
		}
		bcsStarter.syncInvoiceStorage(invoiceId, null, null);
		return RestResultDto.success();
	}

	/**
	 * 重置发货单消耗
	 *
	 * @param invoiceId
	 * @return
	 */
	@RequestMapping(value = "/reset/expend/{invoiceId}", method = RequestMethod.PUT)
	@ResponseBody
	public RestResultDto resetExpend(@PathVariable Long invoiceId) {
		if (invoiceId == null) {
			return RestResultDto.fail(Code.PARAM_EMPTY);
		}
		if (!isAdmin()) {
			//	不是管理员
			return RestResultDto.fail(Code.NO_PURVIEW);
		}
		bcsStarter.resetInvoiceExpend(invoiceId);
		return RestResultDto.success();
	}


	/**
	 * 编辑发货单
	 * @param invoiceId
	 * @param sendStere
	 * @return
	 */
	@RequestMapping(value = "/edit/sendStere/{invoiceId}", method = RequestMethod.PUT)
	@ResponseBody
	public RestResultDto editSendStere(@PathVariable Long invoiceId, Double sendStere) {
		if (invoiceId == null) {
			return RestResultDto.fail("请输入发货单");
		}
		if (sendStere == null || sendStere < 0) {
			return RestResultDto.fail("请输入发货方量");
		}
		//	查询发货单
		ProductionInvoice invoice = invoiceService.getById(invoiceId);
		if (invoice == null) {
			return RestResultDto.fail("发货单不存在或者被删除");
		}
		//	发货方量
		Double proStere = invoiceService.queryProStere(erpDb, invoiceId);
		if (sendStere < proStere) {
			return RestResultDto.fail("发货方量不能小于生产方量");
		}
		invoice.setSendStere(sendStere);
		invoiceService.updateById(invoice);
		return RestResultDto.success();
	}

	/**
	 * 从备份的工控数据中重新匹配发货单缺失的消耗仓位
	 *
	 * @param invoiceId
	 * @return
	 */
	/*@RequestMapping(value = "/sync/storage/backup/{invoiceId}", method = RequestMethod.PUT)
	@ResponseBody*/
	public RestResultDto syncBackupExpend(@PathVariable Long invoiceId) {
		if (invoiceId == null) {
			return RestResultDto.fail(Code.PARAM_EMPTY);
		}
		if (!isAdmin()) {
			return RestResultDto.fail("您不是管理员,没有权限");
		}
		bcsStarter.syncBackStorage(invoiceId, null, null);
		return RestResultDto.success();
	}


	/**
	 * 查询任务单的累计信息和运输车辆
	 *
	 * @param indentId
	 * @return
	 */
	@RequestMapping(value = "/accuAndTran", method = RequestMethod.POST)
	@ResponseBody
	public RestResultDto accuAndTran(Long indentId) {
		if (indentId == null) {
			return RestResultDto.fail("未选择任务单信息");
		}
		String accuAndTranKey = DisCoordinator.accuAndTranKey + "_" + indentId;
		//	从缓存中取
		Object o = null;
		try {
			o = memoryCache.get(accuAndTranKey);
		} catch (Exception e) {
			e.printStackTrace();
		}
		if (o == null) {
			//	没有数据,从新查询
			o = disCoordinator.accuAndTran(indentId);
		}
		//	查询车辆编号
		return RestResultDto.success(o);
	}

}
